package furns.web;

import furns.entity.Furn;
import furns.entity.Page;
import furns.service.FurnService;
import furns.service.impl.FurnServiceImpl;
import furns.utils.DataUtils;
import furns.utils.WebUtils;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.List;
import java.util.UUID;

@SuppressWarnings("all")
@WebServlet(name = "FurnServlet", urlPatterns = "/manage/furnServlet") //为了方便后续的过滤操作
//当接收到浏览器发出的请求后，会首先到该类中调用doGet/doPost方法。如果该类中不存在doGet/doPost方法，则会调用父类中的doGet/doPost方法。
public class FurnServlet extends BasicServlet {
    //创建 furnService对象，便于调用其方法
    private FurnService furnService = new FurnServiceImpl();

    /**
     * 查询所有家具信息
     * 使用 模板设计模式+反射+动态绑定 来调用到list方法
     *
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    protected void list(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //调用方法得到家具信息的集合
        List<Furn> furns = furnService.queryFurns();
        //将家具集合存放到request域中
        request.setAttribute("furns", furns);
        //请求转发
        request.getRequestDispatcher("/views/manage/furn_manage.jsp").forward(request, response);
    }

    /**
     * 保存家具信息
     *
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    protected void save(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //使用第一种方式完成将前端提交的数据，封装成Furn的Javabean对象
        //获取用户输入的家具信息
//        String name = request.getParameter("name");
//        String maker = request.getParameter("maker");
//        String price = request.getParameter("price");
//        String sales = request.getParameter("sales");
//        String stock = request.getParameter("stock");

        //假如要在后端对用户添加的家居信息进行验证，可以采用以下方式：
        //(1)使用正则表达式验证用户输入的家具信息是否合法。
        //(2)对用户输入的 每一项信息 进行try-catch语句进行异常捕获。若存在异常，则在catch语句中提示用户输入信息不合法，并返回到添加页面。
        //(3)直接对 创建家居实例对象的语句（下面的语句）进行try-catch语句进行异常捕获。若存在异常，则在catch语句中提示用户输入信息不合法，并返回到添加页面。

//        Furn furn = new Furn(null, name, maker, new BigDecimal(price), new Integer(sales), new Integer(stock), "assets/images/product-image/default.jpg");

        //使用第二种方式完成将前端提交的数据，封装成Furn的Javabean对象
        //使用BeanUtils完成javabean对象的自动封装
//        Furn furn = new Furn();
//        try {
//            //老韩解读  request.getParameterMap() 将用户输入的家居数据封装到furn 对象
//            //使用反射将数据封装, 有一个前提就是表单提交的数据字段名，需要和封装的Javabean的属性名一致
//            //<input name="maker" style="width: 90%" type="text" value=""/>
//            BeanUtils.populate(furn, request.getParameterMap());
//        } catch (Exception e) {
//            throw new RuntimeException(e);
//        }
        //使用工具类将前端提交的数据，封装成Furn的Javabean对象
        Furn furn = DataUtils.copyParamToBean(request.getParameterMap(), new Furn());
        //调用方法完成保存
        furnService.saveFurn(furn);

//        //请求转发到家具显示页面。--->由于此时数据库中的家具信息已经发生变化，因此需要重新查询一次（即list方法）。
//        request.getRequestDispatcher("/manage/furnServlet?action=list").forward(request, response);

        //说明：使用请求转发时，当用户刷新页面时，会重新发送一次添加请求，导致数据重复提交。
        //解决方案：使用 重定向
        //由于重定向是浏览器重新发送请求，因此需要填写完整的地址。
//        response.sendRedirect(request.getContextPath() + "/manage/furnServlet?action=list");
        //考虑分页的情况，并带上pageNo。完成添加后，仍然回到当前页。
        response.sendRedirect(request.getContextPath() + "/manage/furnServlet?action=page&pageNo=" + request.getParameter("pageNo"));
    }

    /**
     * 删除家居信息
     *
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    protected void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //首先获取用户将要删除的家居编号
        //老韩解读
        //为了防止接收的id 不是一个数字的字符串 比如"hello"
        //我就使用一个工具方法, 如果是可以转成数字的字符串，就转，否则就返回一个默认值
        int id = DataUtils.parseInt(request.getParameter("id"), 0);
        furnService.deleteFurnById(id);
        //说明：使用请求转发时，当用户刷新页面时，会重新发送一次删除请求，导致数据重复删除。
        //解决方案：使用 重定向
        //由于重定向是浏览器重新发送请求，因此需要填写完整的地址。
//        response.sendRedirect(request.getContextPath() + "/manage/furnServlet?action=list");
        //考虑分页的情况，并带上pageNo。完成删除后，仍然回到当前页。
        response.sendRedirect(request.getContextPath() + "/manage/furnServlet?action=page&pageNo=" + request.getParameter("pageNo"));
    }

    /**
     * 查询单个家居信息并回显
     *
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    protected void query(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //首先获取用户将要查询的家居编号
        //老韩解读
        //为了防止接收的id 不是一个数字的字符串 比如"hello"
        //使用一个工具方法, 如果是可以转成数字的字符串，就转，否则就返回一个默认值
        int id = DataUtils.parseInt(request.getParameter("id"), 0);
        Furn furn = furnService.queryFurnById(id);
        //将查询到的家居信息存放到request域中
        request.setAttribute("furn", furn);
        //方式一：将pageNo的值存放到request域中
//        request.setAttribute("pageNo", request.getParameter("pageNo"));
        //方式二：如果当前请求带来的参数 pageNo=1，是通过请求转发到下一个页面，
        //       则在下一个页面可以直接使用param.pageNo取出该值。
        //请求转发到家居更新页面
        request.getRequestDispatcher("/views/manage/furn_update.jsp").forward(request, response);
    }

    /**
     * 修改家居信息,但不包括修改图片
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
//    protected void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//        //使用工具类完成将前端提交的数据，封装成Furn的Javabean对象
//        Furn furn = DataUtils.copyParamToBean(request.getParameterMap(), new Furn());
//        //调用方法完成保存
//        furnService.updateFurn(furn);
//
////        //请求转发到家具显示页面。--->由于此时数据库中的家具信息已经发生变化，因此需要重新查询一次（即list方法）。
////        request.getRequestDispatcher("/manage/furnServlet?action=list").forward(request, response);
//
//        //说明：使用请求转发时，当用户刷新页面时，会重新发送一次修改请求，导致数据重复修改。
//        //解决方案：使用 重定向
//        //由于重定向是浏览器重新发送请求，因此需要填写完整的地址。
////        response.sendRedirect(request.getContextPath() + "/manage/furnServlet?action=list");
//        //考虑分页的情况，并带上pageNo。完成修改后，仍然回到当前页。
//        response.sendRedirect(request.getContextPath() + "/manage/furnServlet?action=page&pageNo=" + request.getParameter("pageNo"));
//    }

    /**
     * 修改家具信息，包括 对 家居图片的修改
     *
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    protected void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //注：当表单是 enctype="multipart/form-data" 时，无法通过 request.getParameter("id") 获取放在隐藏域中的id数据。但可以获取到存放在url中的id值。
        //可以获取：<form action="manage/furnServlet?id=${requestScope.furn.id}&action=update&pageNo=${param.pageNo}" method="post" enctype="multipart/form-data">
        //无法获取：<input type="hidden" name="id" value="${requestScope.furn.id}">
        //获取到家居id,并使用工具类将其转换为int类型。
        int id = DataUtils.parseInt(request.getParameter("id"), 0);
        //根据家居id获取到相应的家居对象
        Furn furn = furnService.queryFurnById(id);
        if (furn == null) {
            return;
        }
        //1. 判断是不是文件表单(enctype="multipart/form-data")
        if (ServletFileUpload.isMultipartContent(request)) {
            //2. 创建 DiskFileItemFactory 对象, 用于构建一个解析上传数据的工具对象
            DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
            //3. 创建一个解析上传数据的工具对象
            ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
            //解决接收到文件名是中文乱码问题
            servletFileUpload.setHeaderEncoding("utf-8");

            //4. 关键的地方, servletFileUpload 对象可以把表单提交的数据 text/文件 ，将其封装到 FileItem 文件项中
            try {
                List<FileItem> list = servletFileUpload.parseRequest(request);
                //遍历，并分别处理=> 自然思路
                for (FileItem fileItem : list) {
                    //判断是不是一个文件
                    if (fileItem.isFormField()) {//如果是true就是文本 即文本 <input type="text" name="name"><br/> (普通的表单字段)
                        if ("name".equals(fileItem.getFieldName())) {//家居名
                            furn.setName(fileItem.getString("utf-8"));
                        } else if ("maker".equals(fileItem.getFieldName())) {//制造商
                            furn.setMaker(fileItem.getString("utf-8"));
                        } else if ("price".equals(fileItem.getFieldName())) {//价格
                            furn.setPrice(new BigDecimal(fileItem.getString()));
                        } else if ("sales".equals(fileItem.getFieldName())) {//销量
                            furn.setSales(new Integer(fileItem.getString()));
                        } else if ("stock".equals(fileItem.getFieldName())) {//库存
                            furn.setStock(new Integer(fileItem.getString()));
                        }
                    } else {//是一个文件
                        //获取上传的文件的名字
                        String name = fileItem.getName();

                        //如果用户没有选择新的图片, name = ""
                        if (!"".equals(name)) {   //只有当用户更换了新的图片时才执行下面的代码
                            //1.指定一个目录 , 就是我们网站工作目录下。指定目录时，开头的 /  可以带，也可以不带。
                            // 如果还想在该路径下创建文件夹，则必须使用 /  结尾。  如果不以 / 结尾，则表示上传的文件就存放在该目录下
                            String filePath = "/" + WebUtils.FURN_IMG_DIRECTORY + "/";
//                            String filePath = WebUtils.FURN_IMG_DIRECTORY;   也能成功运行
                            //2. 获取到完整目录 [io/servlet基础]  即该web项目真正的工作目录
                            //request.getServletContext().getRealPath() 该方法得到的真正工作路径的最后 带有 \
                            String fileRealPath =
                                    request.getServletContext().getRealPath(filePath); //相当于将filPath表示的路径，放到真正工作路径下
                            //3. 创建这个上传的目录=> 创建目录?=> Java基础
                            File fileRealPathDirectory = new File(fileRealPath + WebUtils.getYearMonthDay());
                            if (!fileRealPathDirectory.exists()) {//不存在，就创建
                                fileRealPathDirectory.mkdirs();//创建
                            }
                            //4. 将文件拷贝到fileRealPathDirectory目录
                            //   构建一个上传文件的完整路径 ：目录+文件名
                            //   对上传的文件名进行处理, 前面增加一个前缀，保证是唯一即可, 不错
                            name = UUID.randomUUID().toString() + "_" + System.currentTimeMillis() + "_" + name;
                            String fileFullPath = fileRealPathDirectory + "/" + name;
                            fileItem.write(new File(fileFullPath)); //保存

                            fileItem.getOutputStream().close();//关闭流
                            //删除原来旧的不用的图片
//                            if (furn.getImgPath() != null) {
//                                String oldImgPath = request.getServletContext().getRealPath(furn.getImgPath());
////                                System.out.println("oldImgPath = " + oldImgPath);
//                                File file = new File(oldImgPath);
////                                System.out.println("file = " + file);
//                                if (file.exists()) {
//                                    file.delete();
//                                }
//                            }
                            //更新家居的图片路径
                            furn.setImgPath(WebUtils.FURN_IMG_DIRECTORY + "/" + name);

                        }
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("不是文件表单...");
        }

        //更新furn对象->DB
        furnService.updateFurn(furn);
        //可以请求转发到更新成功的页面..
        request.getRequestDispatcher("/views/manage/update_ok.jsp").forward(request, response);

    }


    /**
     * 分页显示家具信息
     *
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    protected void page(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int pageNo = DataUtils.parseInt(request.getParameter("pageNo"), 1);
        int pageSize = DataUtils.parseInt(request.getParameter("pageSize"), Page.PAGE_SIZE);
        //调用service方法，获取page对象
        Page<Furn> page = furnService.page(pageNo, pageSize);
        //将page对象放入request域
        request.setAttribute("page", page);
        //请求转发到furn_manage.jsp页面，并在该页面中展示page的数据
        request.getRequestDispatcher("/views/manage/furn_manage.jsp").forward(request, response);
    }

}